// 只依赖s94-js的工具封装
import s94Mjs from "s94-js";

/**
 * 统一的环境变量获取方法，优先级：s94_env > vite 的环境变量(忽略前缀) > Webpack/Node.js 的环境变量
 * @param {string} key
 * @param {*} [def]
 */
function unifyEnv(key, def){
    let result = undefined;
    if (typeof s94_env === 'function'){
        result = s94_env(key);
    }
    // vite 的环境变量
    if (typeof result === 'undefined' && typeof import.meta !== 'undefined' && import.meta.env){
        result = import.meta.env['VITE_'+key];
    }
    // Webpack/Node.js 的环境变量
    if (typeof result === 'undefined' && typeof process !== 'undefined' && process.env){
        result = process.env[key];
    }
    return typeof result === 'undefined' ? def : result;
}

function _href(path, base){
    return _url_api(path, base);
}
function _url_api(path, base){
    base = base || unifyEnv('API_HOST', window.location.origin);
    return s94Mjs.url(path, base).href;
}
function _url_static(path, base){
    base = base || unifyEnv('STATIC_HOST');
    return _url_api(path, base);
}
function _get(key, def){
    let search = s94Mjs.to_string(window?.location?.search).substring(1);
    // 兼容vue-router的hash模式路由
    let hash = s94Mjs.to_string(window?.location?.hash);
    if (~hash.indexOf('?')){
        search = [hash.replace(/^[^\?]*\?/,'') ,search].join('&');
    }
    if (!search) return def;
    search = s94Mjs.param_decode(search);
    let res = s94Mjs.map(search, key, def);
    return res;
}


/**获取系统类型值
 * @returns {number}
 */
function get_os(){
    let res = 0;
    if (typeof window.navigator !== 'object') return res;
    if (/MicroMessenger/i.test(window.navigator.userAgent)) res |= s94Mjs.OS_H5_WECHAT;
    if (/Android/i.test(window.navigator.userAgent)) {
        res |= s94Mjs.OS_H5_ANDROID;
    }else if (/iPhone|iPad/i.test(window.navigator.userAgent)){
        res |= s94Mjs.OS_H5_IOS
    }else{
        res |= s94Mjs.OS_H5_PC
    }
    return res;
}
function s94_log(obj, note, depth){
    //初始化dom
    if(!s94_log.outer) s94_log.outer = s94_log.init();
    //渲染数据
    function loop(data, depth) {
        var html = '';
        if(typeof data === 'object') {
            if(depth >= 0) {
                html += '<i onclick="' + s94_log.outer.openfunc + '(this)" style="">-</i><ul index="' + (depth%3) + '">';
                for(var k in data) {
                    html += '<li>' + k + ' : ' + loop(data[k], (depth - 1)) + '</li>';
                }
                html += '</ul>';
            } else {
                html += Object.prototype.toString.call(data);
            }
        } else {
            html = '<xmp>' + data + '</xmp>';
        }
        return html;
    }
    if (!note){
        let now = new Date();
        note = (now.getMinutes()+'').padStart(2,'0')+':'+(now.getSeconds()+'').padStart(2,'0')+'.'+(now.getMilliseconds()+'').padStart(3,'0');
    }
    depth = typeof(depth)=='undefined' ? 2 : Number(depth);
    s94_log.outer.style.display = 'block';
    s94_log.outer.dom_content.innerHTML += '<div><span style="color:#007AFF">' + note + '</span>: ' + loop(obj, depth || 2) + '</div>';
}
s94_log.init = function() {
    var boxid = 's94_log' + (new Date()).getTime(),
        outer = document.createElement('div'),
        log_ico = document.createElement('span'),
        exit = document.createElement('p'),
        stl = document.createElement('style'),
        log_txt = document.createElement('div');
    var cssText = '#' + boxid + '{position: fixed;bottom: 0;right: 0;width: 100%;z-index: 999999;background: #fff;color: #000;text-align: left;border-top: 1px solid #999;}' +
        '#' + boxid + ' *{padding:0;margin:0;}' +
        '#' + boxid + '>span{display: block;color:#fff;background: rgba(0,0,0,.3);width: 1em;height: 1em;line-height:1.3em;text-align: center;font-size: 3em;position: fixed;bottom: 0.6em;right: 0.6em;cursor: pointer;}' +
        '#' + boxid + '>div{display: none;background: #fff;margin-top:2em;height: 18em;padding: 0;line-height: 1.5em;overflow:auto;font-size:1em;}' +
        '#' + boxid + '>p{display: none;height: 2em;line-height: 2em;justify-content: space-between;font-size: 1em;position: absolute;top: 0;left: 0.8em;right: 0.8em;cursor: pointer;color: #E74C3C;}' +
        '#' + boxid + '>div>div{margin-top: 1em}' +
        '#' + boxid + ' xmp{font-family: inherit;margin:0;display:inline;}' +
        '#' + boxid + ' i{color:#f00;display: inline-block;width: 1.5em;cursor: pointer;text-align: center;font-style: normal;}' +
        '#' + boxid + ' li{font-size: 1em;list-style-type: inherit;height:auto;line-height:1.2em}' +
        '#' + boxid + ' ul{padding-inline-start: 2em;}' +
        '#' + boxid + ' ul[index="0"]{list-style-type: circle;}' +
        '#' + boxid + ' ul[index="1"]{list-style-type: disc;}' +
        '#' + boxid + ' ul[index="2"]{list-style-type: square;}';

    stl.innerHTML = cssText;
    log_ico.innerText = '^';
    function change(open){
        log_ico.style.display = open ? 'none' : 'block';
        log_txt.style.display = open ? 'block' : 'none';
        exit.style.display = open ? 'flex' : 'none';
    }
    log_ico.addEventListener('click', function() {change(1)});
    exit.innerHTML = '<span>清空</span><span>关闭</span>';
    exit.querySelector('span').addEventListener('click', function() {
        log_txt.innerHTML = '';
        outer.style.display = 'none';
    });
    exit.addEventListener('click', function() {change()});
    outer.id = boxid;
    document.querySelector('head').appendChild(stl);
    document.querySelector('body').appendChild(outer);
    outer.appendChild(log_txt);
    outer.appendChild(exit);
    outer.appendChild(log_ico);
    outer.dom_content = log_txt;

    outer.openfunc = 'open' + boxid;
    window[outer.openfunc] = function(dom) {
        var ul = dom.parentNode.querySelector('ul');
        if(dom.innerText == '-') {
            dom.innerText = '+';
            ul.style.display = 'none';
        } else {
            dom.innerText = '-';
            ul.style.display = 'block';
        }
    }
    return outer;
}

/**
 * 解析config
 * @param data 如果为对象，根据key_map获得选项文本和值；如果为字符串，格式：选项文本|选项值,选项值,选项文本|选项值[子项文本|子项值],...
 * @param [key_map] 指定字段名，格式：{children_key:'子项键值名称，默认为children', content_key:'选项文本字段', value_key:'选项值字段', index_key:'索引字段，和parent联合确定子项', parent_key:'指向父级索引数据的字段'}
 * @returns {*|void} [{content:'选项文本', value: '选项值'},...]
 */
function decode_config(data, key_map){
    if(!data) return [];
    key_map = typeof(key_map)=='object' ? key_map : {};
    let children_key = key_map.children_key || 'children';
    if (data._is_config_) return data;
    var res = typeof(data) == 'string' ? str_to_config(data) : arr_to_config(data, key_map);
    Object.defineProperty(res, '_is_config_', {value:true})
    return res;

    function arr_to_config(arr, key_map){
        if (typeof(arr)!='object') return [];
        key_map.content_key = key_map.content_key || 'content';
        key_map.value_key = key_map.value_key || 'value';
        key_map.parent_key = key_map.parent_key || '';
        if (!key_map.index_key) {
            let ms = key_map.parent_key.match(/^parent_(.+)$/);
            key_map.index_key = ms ? ms[1] : '';
        }
        let child_index = [];
        function loop(list, parent_index){
            var res=[], i=-1;
            if (!list || typeof(list)!='object') return res;
            for (const k in list) {
                i++;
                let v = list[k];
                if (typeof(parent_index)!='undefined'){
                    if (v[key_map.parent_key]!==parent_index) continue;
                    if(!~child_index.indexOf(i)) child_index.push(i);
                }
                let row = {};
                if (typeof(v)=='object'){
                    row = Object.assign({},v);
                    row.content = row[key_map.content_key];
                    row.value = row[key_map.value_key]+'';
                    let child_arr = row[children_key] && typeof(row[children_key])=='object' ? loop(row[children_key]) : [];
                    if (key_map.parent_key && key_map.index_key && typeof(row[key_map.index_key])!='undefined'){
                        child_arr = child_arr.concat( loop(list, row[key_map.index_key]) )
                    }
                    if(child_arr.length) {
                        row[children_key] = child_arr;
                    }else {
                        delete row[children_key];
                    }
                }else {
                    row['content'] = v;
                    row['value'] = k+'';
                }
                res.push(row);
            }
            return res;
        }
        let list = loop(arr);
        child_index = child_index.sort((a,b)=>{return a-b;});
        for (let i = 0; i < child_index.length; i++) {
            list.splice(child_index[i]-i,1);
        }
        return list;
    }

    function str_to_config(str){
        let child_arr = [], res;
        let label_children = '__children_index__';
        function loop_replace(v){
            let has = false;
            v = v.replace(/\[([^\[\]]+)\]/g, function(ms,m1){
                has = true;
                return label_children+(child_arr.push(m1)-1)+label_children;
            })
            return has ? loop_replace(v) : v;
        }
        function loop_decode(str){
            let arr = [];
            str.split(',').forEach(function(v){
                let value,content;
                let i = v.indexOf('|');
                if (i===-1){
                    value = v;
                }else {
                    content = v.substring(0, i);
                    value = v.substring(i+1, v.length);
                }
                let child_index = false;
                let has_child = new RegExp(label_children+'(\\d+)'+label_children);
                value = value.replace(has_child, function(ms,m1){
                    child_index = m1;
                    return '';
                })
                let row = {content:typeof content !== 'undefined'?content:value, value:value}
                if (child_index!==false) row[children_key] = loop_decode(child_arr[child_index]);
                arr.push(row);
            })
            return arr;
        }
        res = str ? loop_decode(loop_replace(str)) : [];
        return res;
    }
}

function decode_btns(btns){
    let res = [];
    switch (Object.prototype.toString.call(btns)){
        case '[object Object]':{
            for (let k in btns) {
                if (typeof(btns[k]) != 'function') continue;
                let arr = k.split('_');
                let row = {title: arr[0], onclick: btns[k]};
                if (arr[1]) row.type = arr[1];
                if (arr[2]) row.size = arr[2];
                res.push(row);
            }
        }break;
        case '[object Array]': {
            btns.forEach((v)=>{
                if (!v.title || typeof(v.onclick) != 'function') return;
                res.push(v);
            })
        }break;
    }
    return res;
}

function radio(options, on_key, ok, fail){
    Object.keys(options).forEach(k=>{
        Object.assign(options[k], k==on_key ? ok : fail);
    })
}

/**对比两个变量是否相等
 * @param {*} p1
 * @param {*} p2
 * @param {string} [mode] 三种模式，===(全等|默认)，==(等于)，*undefined*(未定义全匹配,*表示哪个参数可以未定义)
 * @returns {boolean}
 */
function eq(p1, p2, mode){
    let exp;
    switch (mode) {
        case '==':{
            exp = function (v1,v2){ return v1==v2; }
        }break;
        case '*undefined*':{
            exp = function (v1,v2){ return v1===undefined || v2===undefined || v1==v2; }
        }break;
        case '*undefined':{
            exp = function (v1,v2){ return (v1===undefined && v2!==undefined) || v1==v2; }
        }break;
        case 'undefined*':{
            exp = function (v1,v2){ return (v1!==undefined && v2===undefined) || v1==v2; }
        }break;
        default:{
            exp = function (v1,v2){ return v1===v2; }
        }break;
    }
    if (typeof p1 !== typeof p2) return exp(p1, p2);
    let res = true;
    switch (typeof p1) {
        case "object":{
            let keys = Object.keys(Object.assign({},p1,p2));
            for (let i = 0,k; i < keys.length; i++) {
                k = keys[i];
                res = exp(p1[k], p2[k]);
                if (!res) console.log(k, p1[k], p2[k], res);
                if (!res) break;
            }
        }break;
        default: {
            res = exp(p1, p2);
        }break;
    }
    return res;
}

function copy_txt(text){
    try {
        window.navigator.clipboard.writeText(text);
    } catch (e) {
        const input = document.createElement('input');
        input.value = text;
        // 将输入框添加到页面，但不显示
        document.body.appendChild(input);
        input.select(); // 选择输入框的内容
        // 执行复制命令
        document.execCommand('copy');
        // 移除临时输入框
        document.body.removeChild(input);
    }
}

/**图片处理
 * @param {Image} img 图片对象
 * @param {SVGImageElement} img
 * @param {HTMLVideoElement} img
 * @param {HTMLCanvasElement} img
 * @param {ImageBitmap} img
 * @param {Blob} img
 * @param {String} img
 * @param {Number} op 仅配置输出宽度
 * @param {Number} op.width 输出宽度，默认为自动
 * @param {Number} op.height 输出高度，默认为自动
 * @param {String} op.fit 填充模式：contain(居中保持宽高比部分留空|默认)，cover(居中保持宽高比部分剪切)，fill(契合输出宽高比铺满)，scale-down(原尺寸和contain之间取更小的)
 * @param {String} op.to 导出数据类型：blob，base64(默认)
 * @param {String} op.type 导出图片类型：jpeg，png(默认)
 * @param {String} op.blank 留空的地方的处理，可选值：cut(剪切)，颜色值(纯色背景)，默认情况下：根据导出类型png透明，jpg白色背景
 * @returns {Promise<unknown>}
 */
function img_handle(img, op){
    if (!img_handle.canvas){
        img_handle.canvas = document.createElement('canvas');
        // img_handle.canvas.style.visibility='hidden';
        img_handle.canvas.style.display='none';
        document.documentElement.append(img_handle.canvas);
    }
    let type_map = ['image/png','image/jpeg'];
    let default_type = 'image/png';
    return (new Promise((resolve)=>{
        if (typeof op !== 'object') op = {width:op};
        if (typeof op.width === 'undefined' && typeof op.height === 'undefined') throw '输出图片的width和height至少设定一个';
        if (img instanceof Image || img instanceof HTMLImageElement || img instanceof SVGImageElement || img instanceof HTMLVideoElement || img instanceof HTMLCanvasElement || img instanceof ImageBitmap){
            if (img.src){
                let suffix = img.src.replace(/\?.*$/,"").split('.').pop();
                if (~['jpg','jpeg'].indexOf(suffix)) default_type = 'image/jpeg';
            }
            resolve(img);
        }else{
            let img_url = typeof img === 'string' ? img : '';
            if (img instanceof Blob){
                img_url = URL.createObjectURL(img);
            }
            if (!img_url) throw '输入图片img参数类型错误';
            let img_obj = new Image();
            img_obj.crossOrigin = 'anonymous';
            img_obj.src = img_url;
            img_obj.onload = function (){
                resolve(img_obj);
                if (img instanceof Blob) {
                    URL.revokeObjectURL(img_url);
                    if (~type_map.indexOf(img.type)) default_type = img.type;
                }else {
                    let suffix = img_url.replace(/\?.*$/,"").split('.').pop();
                    if (~['jpg','jpeg'].indexOf(suffix)) default_type = 'image/jpeg';
                }
            }
        }
    })).then((img)=>{
        let canvas = img_handle.canvas;
        let img_w = img.width;
        let img_h = img.height;
        let img_wh = img.width / img.height;

        let auto_wh = true;
        if (typeof op.width === 'undefined'){
            op.width = Math.round(op.height * img_wh);
        }else if(typeof op.height === 'undefined'){
            op.height = Math.round( op.width / img_wh);
        }else {
            auto_wh = false;
        }
        let canvas_w = op.width;
        let canvas_h = op.height;
        let canvas_wh = canvas_w / canvas_h;
        let sx=0,sy=0,sw=img_w,sh=img_h;
        let dx=0,dy=0,dw=canvas_w,dh=canvas_h;
        if (!auto_wh){ //不是自适应的宽高的情况，需要计算
            let fit = op.fit || 'contain';
            switch (fit){
                case 'contain':case 'scale-down':{
                    if (fit==='scale-down' && img_w<canvas_w && img_h<canvas_h){
                        dh = img_h;
                        dw = img_w;
                    }else {
                        if (img_wh > canvas_wh){
                            dh = canvas_w/img_wh;
                        }else {
                            dw = canvas_h*img_wh;
                        }
                    }
                    dy = (canvas_h-dh) / 2;
                    dx = (canvas_w-dw) / 2;
                }break;
                case 'cover':{
                    if (canvas_wh > img_wh){
                        sh = img_w/canvas_wh;
                        sy = (img_h-sh) / 2;
                    }else {
                        sw = img_h*canvas_wh;
                        sx = (img_w-sw) / 2;
                    }
                }break;
            }
        }
        if (op.blank === 'cut'){
            canvas_h = dh;
            canvas_w = dw;
            dx = dy = 0;
        }

        canvas.width = canvas_w;
        canvas.height = canvas_h;

        let ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        let type = ~type_map.indexOf(op.type) ? op.type : default_type;
        if (type === 'image/jpeg'){
            ctx.fillStyle = op.blank && /^#[0-f]{6}$/i.test(op.blank) ? op.blank : '#ffffff';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }
        ctx.drawImage(img, sx,sy,sw,sh, dx,dy,dw,dh);
        if (op.to === 'blob'){
            return new Promise((resolve)=>{
                canvas.toBlob(resolve, type);
            });
        }else {
            return canvas.toDataURL(type);
        }
    })
}

/**
 * @param {String} str
 * @returns {string}
 */
function camel_str(str){
    str = s94Mjs.to_string(str);
    str = str.replace(/_(\w)/g, function (ms, m1) {
        return m1.toUpperCase()
    });
    if (!str.length) return str;
    return str[0].toUpperCase() + str.substring(1);
}

function in_right(ca, rights){
    if (typeof rights === 'string') rights = rights.split(',');
    if (typeof ca === 'string') ca = ca.split('/');
    return Object.values(rights).some((row) => {
        let row_ca = row.split('/');
        return (row_ca[0] === '*' || row_ca[0] === ca[0]) && (row_ca[1] === '*' || row_ca[1] === ca[1]);
    });
}

/**
 * @param {String|Array} event_name 事件名，可以用数组指定多个
 * @param {Function} callback_or_data 绑定的事件回调函数
 * @param {Blob} callback_or_data 传入false，表示移除绑定事件
 * @param {*} [callback_or_data] 其他类型，表示事件触发时传入的数据 evt.data
 */
function s94_event(event_name, callback_or_data){
    if (typeof event_name === 'object'){
        return Object.values(event_name).forEach(v=>{ s94_event(v, callback_or_data); })
    }
    if (callback_or_data === false){
        s94_event.remove(event_name);
    }else if (typeof callback_or_data === 'function'){
        s94_event.add(event_name, callback_or_data);
    }else {
        s94_event.action(event_name, callback_or_data);
    }
}
s94_event.listenerMap = {}
/**添加事件监听
 * @param {String} event_name 监听的事件名，支持通配符匹配，例如*_add可以
 * @param {Function} callback 回调函数
 */
s94_event.add = function (event_name, callback){
    if (!s94_event.listenerMap[event_name]) s94_event.listenerMap[event_name] = [];
    s94_event.listenerMap[event_name].push(callback);
}
/**删除事件监听
 * @param {String} event_name 匹配的事件名，与add时传入的event_name匹配
 */
s94_event.remove = function (event_name){
    delete s94_event.listenerMap[event_name];
}
/**执行事件
 * @param {String} event_name 执行的事件名
 * @param {*} data 执行传入的evt.data
 */
s94_event.action = function (event_name, data){
    Object.keys(s94_event.listenerMap).forEach(k=>{
        // 监听的事件名解析通配符
        let reg = new RegExp(`^${k.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')}$`);
        if (!reg.test(event_name)) return;
        let evt = {listener_name:k, event_name:event_name, data:data}
        s94_event.listenerMap[k].forEach(call=>{
            try {
                call(evt);
            } catch (err) {
                console.error('事件执行异常', err);
            }
        })
    })
}

/**json编码，编码失败返回空字符串''
 * @param {*} value 编码的数据
 * @returns {string}
 */
function json_encode(value){
    try {
        return JSON.stringify(value);
    } catch (e) {
        return '';
    }
}

/**
 * json解码，解码失败返回undefined
 * @param {string} json json字符
 * @returns {any|null}
 */
function json_decode(json){
    try {
        return JSON.parse(json);
    } catch (e) {
        return undefined;
    }
}

/**json解码为对象，如果json解码结果不是对象(数组)，返回空对象{}
 * @param {string} json json字符
 * @returns {{}}
 */
function json_decode_object(json){
    let res = json_decode(json);
    if (!res || typeof res !== 'object')  res = {};
    return res;
}

/**
 * @param name_kv
 * @param {*} def_timeout 默认值或者过期时间(秒)
 * @returns {*}
 */
function cache(name_kv, def_timeout){
    return s94Mjs.cache(name_kv, def_timeout, sessionStorage);
}

function cache_local(name_kv, def_timeout){
    return s94Mjs.cache(name_kv, def_timeout, localStorage);
}

/**url参数编码，
 * @param {Object} obj 编码的参数：例如：{a:1,b:2,c:[3]} => a=1&b=2&c[0]=3
 * @returns {string}
 */
function param_encode(obj){
    return s94Mjs.param_encode(obj);
}

/**url参数解码
 * @param {string} str url参数字符串，例如：a=1&b=2&c[0]=3 => {a:1,b:2,c:[3]}
 * @returns {Object}
 */
function param_decode(str){
    return s94Mjs.param_decode(str);
}

function to_string(value){
    return s94Mjs.to_string(value);
}

function map(data, key_data, def){
    return s94Mjs.map(data, key_data, def);
}

/**防抖
 * @param {Function} func
 * @param {number} delay 防抖时长，毫秒
 * @param {boolean} [immediate] 是否为理解执行模式
 * @returns {(function(...[*]): void)|*}
 */
function debounce(func, delay, immediate = false) {
    let timeoutId;

    return function(...args) {
        const context = this;

        // 清除之前的定时器
        clearTimeout(timeoutId);

        if (immediate && !timeoutId) {
            // 立即执行
            func.apply(context, args);
        }

        timeoutId = setTimeout(() => {
            timeoutId = null;
            if (!immediate) {
                func.apply(context, args);
            }
        }, delay);
    };
}

export {
    unifyEnv, _url_api, _url_static, _href, _get, //基础
    s94_log, s94_event, //自定义独有方法
    param_encode,param_decode,cache,cache_local, //94的方法直接引用
    get_os,decode_config, decode_btns,eq,copy_txt,img_handle,camel_str,in_right,
    json_encode,json_decode,json_decode_object, //json优化
    to_string, map,
    debounce,
};